//<MStar Software>
//******************************************************************************
// MStar Software
// Copyright (c) 2010 - 2020 MStar Semiconductor, Inc. All rights reserved.
// All software, firmware and related documentation herein ("MStar Software") are
// intellectual property of MStar Semiconductor, Inc. ("MStar") and protected by
// law, including, but not limited to, copyright law and international treaties.
// Any use, modification, reproduction, retransmission, or republication of all
// or part of MStar Software is expressly prohibited, unless prior written
// permission has been granted by MStar.
//
// By accessing, browsing and/or using MStar Software, you acknowledge that you
// have read, understood, and agree, to be bound by below terms ("Terms") and to
// comply with all applicable laws and regulations:
//
// 1. MStar shall retain any and all right, ownership and interest to MStar
//    Software and any modification/derivatives thereof.
//    No right, ownership, or interest to MStar Software and any
//    modification/derivatives thereof is transferred to you under Terms.
//
// 2. You understand that MStar Software might include, incorporate or be
//    supplied together with third party's software and the use of MStar
//    Software may require additional licenses from third parties.
//    Therefore, you hereby agree it is your sole responsibility to separately
//    obtain any and all third party right and license necessary for your use of
//    such third party's software.
//
// 3. MStar Software and any modification/derivatives thereof shall be deemed as
//    MStar's confidential information and you agree to keep MStar's
//    confidential information in strictest confidence and not disclose to any
//    third party.
//
// 4. MStar Software is provided on an "AS IS" basis without warranties of any
//    kind. Any warranties are hereby expressly disclaimed by MStar, including
//    without limitation, any warranties of merchantability, non-infringement of
//    intellectual property rights, fitness for a particular purpose, error free
//    and in conformity with any international standard.  You agree to waive any
//    claim against MStar for any loss, damage, cost or expense that you may
//    incur related to your use of MStar Software.
//    In no event shall MStar be liable for any direct, indirect, incidental or
//    consequential damages, including without limitation, lost of profit or
//    revenues, lost or damage of data, and unauthorized system use.
//    You agree that this Section 4 shall still apply without being affected
//    even if MStar Software has been modified by MStar in accordance with your
//    request or instruction for your use, except otherwise agreed by both
//    parties in writing.
//
// 5. If requested, MStar may from time to time provide technical supports or
//    services in relation with MStar Software to you for your use of
//    MStar Software in conjunction with your or your customer's product
//    ("Services").
//    You understand and agree that, except otherwise agreed by both parties in
//    writing, Services are provided on an "AS IS" basis and the warranty
//    disclaimer set forth in Section 4 above shall apply.
//
// 6. Nothing contained herein shall be construed as by implication, estoppels
//    or otherwise:
//    (a) conferring any license or right to use MStar name, trademark, service
//        mark, symbol or any other identification;
//    (b) obligating MStar or any of its affiliates to furnish any person,
//        including without limitation, you and your customers, any assistance
//        of any kind whatsoever, or any information; or
//    (c) conferring any license or right under any intellectual property right.
//
// 7. These terms shall be governed by and construed in accordance with the laws
//    of Taiwan, R.O.C., excluding its conflict of law rules.
//    Any and all dispute arising out hereof or related hereto shall be finally
//    settled by arbitration referred to the Chinese Arbitration Association,
//    Taipei in accordance with the ROC Arbitration Law and the Arbitration
//    Rules of the Association by three (3) arbitrators appointed in accordance
//    with the said Rules.
//    The place of arbitration shall be in Taipei, Taiwan and the language shall
//    be English.
//    The arbitration award shall be final and binding to both parties.
//
//******************************************************************************
//<MStar Software>

package com.mstar.android.media;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.FileDescriptor;

import android.graphics.Bitmap;
import android.media.MediaHTTPService;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.media.MediaMetadataRetriever;

import androidx.annotation.RequiresApi;

public class MMediaPlayer extends MediaPlayer {
    public class InitParameter {
        public float degrees = 0;
        public float scaleX = 1;
        public float scaleY = 1;
        public int cropX = 0;
        public int cropY = 0;
        public int cropWidth = 0;
        public int cropHeight = 0;
    }

    public enum FileInfo {
        StringPath,
        FileDes
    }

    static {
        System.loadLibrary("mmedia_jni");
        native_init();
    }

    private final static String TAG = "MMediaPlayer";

    private final static String IMEDIA_PLAYER = "android.media.IMediaPlayer";

    private long mNaticeSubtitleSurfaceTexture;

    private SurfaceHolder mSubtitleSurfaceHolder;

    private static final int KEY_PARAMETER_DEMUX_RESET = 2000;

    private static final int KEY_PARAMETER_SET_SEAMLESS_MODE = 2001;

    private static final int KEY_PARAMETER_DATASOURCE_SWITCH = 2002;

    private static final int KEY_PARAMETER_PAYLOAD_SHOT = 2003;

    private static final int KEY_PARAMETER_SWITCH_TO_PUSH_DATA_MODE = 2004;

    private static final int KEY_PARAMETER_CHANGE_PROGRAM = 2005;

    private static final int KEY_PARAMETER_MEDIA_CREATE_THUMBNAIL_MODE = 2006;

    private static final int KEY_PARAMETER_VIDEO_ONLY_MODE = 2007;

    private static final int KEY_PARAMETER_AVCODEC_CHANGED = 2008;

    private static final int KEY_PARAMETER_SET_TS_INFO = 2009;

    private static final int KEY_PARAMETER_ACCEPT_NEXT_SEAMLESS = 2010;

    private static final int KEY_PARAMETER_SET_SUBTITLE_INDEX = 2011;

    private static final int KEY_PARAMETER_GET_SUBTITLE_TRACK_INFO = 2012;

    private static final int KEY_PARAMETER_GET_ALL_SUBTITLE_TRACK_INFO = 2013;

    private static final int KEY_PARAMETER_SET_RESUME_PLAY = 2014;

    private static final int KEY_PARAMETER_GET_DIVX_DRM_IS_RENTAL_FILE = 2015;

    private static final int KEY_PARAMETER_GET_DIVX_DRM_RENTAL_LIMIT = 2016;

    private static final int KEY_PARAMETER_GET_DIVX_DRM_RENTAL_USE_COUNT = 2017;

    private static final int KEY_PARAMETER_GET_BUFFER_STATUS = 2018;

    private static final int KEY_PARAMETER_SET_VIDEO_DECODE_ALL = 2019;

    private static final int KEY_PARAMETER_SET_VIDEO_DECODE_I_ONLY = 2020;

    private static final int KEY_PARAMETER_CHANGE_AV_SYNCHRONIZATION = 2021;

    private static final int KEY_PARAMETER_GET_CONTAINER_PTS = 2022;

    private static final int KEY_PARAMETER_SET_LOW_LATENCY_MODE = 2023;

    private static final int KEY_PARAMETER_SET_DUAL_DECODE_PIP = 2024;

    private static final int KEY_PARAMETER_GET_TITLE_COUNT = 2025;

    private static final int KEY_PARAMETER_GET_ALL_TITLE_NAME = 2026;

    private static final int KEY_PARAMETER_GET_EDITION_COUNT = 2027;

    private static final int KEY_PARAMETER_GET_ALL_EDITION_NAME = 2028;

    private static final int KEY_PARAMETER_GET_AUTHOR_CHAPTER_COUNT = 2029;

    private static final int KEY_PARAMETER_GET_ALL_CHAPTER_NAME = 2030;

    private static final int KEY_PARAMETER_GET_LAW_RATING = 2031;

    private static final int KEY_PARAMETER_GET_ALL_AUDIO_TRACK_INFO = 2032;

    private static final int KEY_PARAMETER_GET_TITLE_EDITION = 2033;

    private static final int KEY_PARAMETER_GET_CHAPTER = 2034;

    private static final int KEY_PARAMETER_SET_TITLE_EDITION = 2035;

    private static final int KEY_PARAMETER_SET_CHAPTER = 2036;

    private static final int KEY_PARAMETER_GET_ACTIVE_AUDIO_TRACK_INFO = 2037;

    private static final int KEY_PARAMETER_GET_ACTIVE_AUDIO_TRACK_NAME = 2038;

    private static final int KEY_PARAMETER_GET_ACTIVE_SUBTITLE_TRACK_NAME = 2039;

    /* Use for MStar Multi-Thumbnail(MT) API. Java app use it to trigger native player for every MT round.
     * The parcel format is:
     *  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *  |                       thumbnail position                      |
     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *  |                       thumbnail number                        |
     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *  |                       thumbnail interval                      |
     *  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     */
    private static final int KEY_PARAMETER_SET_MULTI_THUMBS = 2040;

    /* Use this API to notify native player to load "local playback" display settings */
    private static final int KEY_PARAMETER_SET_PQ_LOCAL = 2041;

    /* Use this API to set rebuffer duration. (unit: second) */
    private static final int KEY_PARAMETER_SET_REBUFFER_DURATION = 2042;

    private static final int KEY_PARAMETER_GET_ALL_AUDIO_TRACK_NAME = 2044;

    private static final int KEY_PARAMETER_GET_ALL_SUBTITLE_TRACK_NAME = 2045;

    // There are 3 different mode now.
    // Set to 1: set minimal rebuffer mode & start player without sync av
    // Set to 2: set minimal rebuffer mode & decoder open process faster
    // Set to 3: set minimal rebuffer mode & decoder open process faster & start player without sync av
    private static final int KEY_PARAMETER_SET_FAST_START_MODE = 2046;

    private static final int KEY_PARAMETER_SET_AUDIO_DROP_THRESHOLD = 2047; // set audio drop threshold (ms) for low latency mode

    private static final int KEY_PARAMETER_SET_IMAGE_SAMPLESIZE_SURFACESIZE = 3000;

    private static final int KEY_PARAMETER_ROTATE = 3001;

    private static final int KEY_PARAMETER_SCALE = 3002;

    private static final int KEY_PARAMETER_ROTATE_SCALE = 3003;

    private static final int KEY_PARAMETER_CROP_RECT = 3004;

    private static final int KEY_PARAMETER_DECODE_NEXT = 3005;

    private static final int KEY_PARAMETER_SHOW_NEXT = 3006;

    private static final int KEY_PARAMETER_GET_AUDIO_TRACK_INFO = 3100;

    private static final int KEY_PARAMETER_GET_DIVX_DRM_INFO = 3101;

    private static final int KEY_PARAMETER_GET_DIVX_RESUME_PLAY = 3102;

    private static final int KEY_PARAMETER_GET_AUDIO_CODEC_TYPE = 3103;

    private static final int KEY_PARAMETER_GET_VIDEO_INFO = 3104;

    private static final int KEY_PARAMETER_GET_AUDIO_INDICATOR = 3105;

    private static final int METADATA_KEY_DIVX_DRM_IS_DEVICE_FIRSTTIME_REG = 26;

    public enum EnumPlayerSeamlessMode {
        // / No seamless mode
        E_PLAYER_SEAMLESS_NONE,
        // / Seamless FreeZ
        E_PLAYER_SEAMLESS_FREEZ,
        // / Seamless Smoth. only for mp4 and flv container
        E_PLAYER_SEAMLESS_SMOTH,
        // Seamless Dynamic scaling
        E_PLAYER_SEAMLESS_DS,
    }

    // Enum of video rotation mode
    public static final int E_VIDEO_ROTATE_INVALID = -1;
    public static final int E_VIDEO_ROTATE_RESTART = 0;
    public static final int E_VIDEO_ROTATE_SMOOTH  = 1;
    public static final int E_VIDEO_ROTATE_DISABLE = 2;

    /* Do not change these values (starting with KEY_PARAMETER) without updating
     * their counterparts in include/media/mediaplayer.h!
     */

    // There are currently no defined keys usable from Java with get*Parameter.
    // But if any keys are defined, the order must be kept in sync with include/media/mediaplayer.h.
    // private static final int KEY_PARAMETER_... = ...;

    /**
     * Sets the parameter indicated by key.
     * @param key key indicates the parameter to be set.
     * @param value value of the parameter to be set.
     * @return true if the parameter is set successfully, false otherwise
     * {@hide}
     */
    public native boolean _setParameter(int key, Parcel value);

    /**
     * Sets the parameter indicated by key.
     * @param key key indicates the parameter to be set.
     * @param value value of the parameter to be set.
     * @return true if the parameter is set successfully, false otherwise
     * {@hide}
     */
    public  boolean setParameter(int key, Parcel value) {
        return _setParameter(key, value);
    }

    /**
     * Sets the parameter indicated by key.
     * @param key key indicates the parameter to be set.
     * @param value value of the parameter to be set.
     * @return true if the parameter is set successfully, false otherwise
     * {@hide}
     */
    public boolean setParameter(int key, String value) {
        Parcel p = Parcel.obtain();
        p.writeString(value);
        boolean ret = _setParameter(key, p);
        p.recycle();
        return ret;
    }

    /**
     * Sets the parameter indicated by key.
     * @param key key indicates the parameter to be set.
     * @param value value of the parameter to be set.
     * @return true if the parameter is set successfully, false otherwise
     * {@hide}
     */
    public boolean setParameter(int key, int value) {
        Parcel p = Parcel.obtain();
        p.writeInt(value);
        boolean ret = _setParameter(key, p);
        p.recycle();
        return ret;
    }

    /*
     * @param request Parcel destinated to the media player. The
     *                Interface token must be set to the IMediaPlayer
     *                one to be routed correctly through the system.
     * @param reply[out] Parcel that will contain the reply.
     * @return The status code.
     * {@hide}
     */
    private native final int _native_invoke(Parcel request, Parcel reply);

    /**
     * Invoke a generic method on the native player using opaque
     * parcels for the request and reply. Both payloads' format is a
     * convention between the java caller and the native player.
     * Must be called after setDataSource to make sure a native player
     * exists. On failure, a RuntimeException is thrown.
     *
     * @param request Parcel with the data for the extension. The
     * caller must use {@link #newRequest()} to get one.
     *
     * @param reply Output parcel with the data returned by the
     * native player.
     * {@hide}
     */
    public void invoke(Parcel request, Parcel reply) {
        int retcode = _native_invoke(request, reply);
        reply.setDataPosition(0);
        if (retcode != 0) {
            throw new RuntimeException("failure code: " + retcode);
        }
    }

    /*
     * Gets the value of the parameter indicated by key.
     * @param key key indicates the parameter to get.
     * @param reply value of the parameter to get.
     */
    private native void getParameter(int key, Parcel reply);

    /**
     * Gets the value of the parameter indicated by key.
     * The caller is responsible for recycling the returned parcel.
     * @param key key indicates the parameter to get.
     * @return value of the parameter.
     * {@hide}
     */
    public Parcel getParcelParameter(int key) {
        Parcel p = Parcel.obtain();
        getParameter(key, p);
        return p;
    }

    /**
     * Gets the value of the parameter indicated by key.
     * @param key key indicates the parameter to get.
     * @return value of the parameter.
     * {@hide}
     */
    public String getStringParameter(int key) {
        Parcel p = Parcel.obtain();
        getParameter(key, p);
        String ret = p.readString();
        p.recycle();
        return ret;
    }

    /**
     * Gets the value of the parameter indicated by key.
     * @param key key indicates the parameter to get.
     * @return value of the parameter.
     * {@hide}
     */
    public int getIntParameter(int key) {
        Parcel p = Parcel.obtain();
        getParameter(key, p);
        int ret = p.readInt();
        p.recycle();
        return ret;
    }

    /**
     * @param
     * @return {@hide}
     * @deprecated This method should not be used.
     */
    @Deprecated
    public boolean SetImageSampleSize(int imageSampleSize) {
        Parcel p = Parcel.obtain();
        p.writeInt(imageSampleSize);
        boolean ret = _setParameter(KEY_PARAMETER_SET_IMAGE_SAMPLESIZE_SURFACESIZE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param imageSampleSize is the number of pixels in either dimension that correspond to a single
     * pixel in the decoded bitmap. For example, imageSampleSize == 4 returns
     * an image that is 1/4 the width/height of the original, and 1/16 the
     * number of pixels. Any value <= 1 is treated the same as 1.
     * @param surfaceWidth is the width of surface.
     * @param surfaceHeight is the height of surface.
     * @return true if success, else return false.
     * @deprecated This method should not be used.
     */
    @Deprecated
    public boolean SetImageSampleSize(int imageSampleSize, int surfaceWidth, int surfaceHeight) {
        Parcel p = Parcel.obtain();
        p.writeInt(imageSampleSize);
        p.writeInt(surfaceWidth);
        p.writeInt(surfaceHeight);
        boolean ret = _setParameter(KEY_PARAMETER_SET_IMAGE_SAMPLESIZE_SURFACESIZE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param imageSampleSize is the number of pixels in either dimension that correspond to a single
     * pixel in the decoded bitmap. For example, imageSampleSize == 4 returns
     * an image that is 1/4 the width/height of the original, and 1/16 the
     * number of pixels. Any value <= 1 is treated the same as 1.
     * @param surfaceWidth is the width of surface.
     * @param surfaceHeight is the height of surface.
     * @param parameter provide more parameters for first show.
     * @return true if success, else return false.
     */
    public boolean SetImageSampleSize(int imageSampleSize, int surfaceWidth, int surfaceHeight, InitParameter parameter) {
        Parcel p = Parcel.obtain();
        p.writeInt(imageSampleSize);
        p.writeInt(surfaceWidth);
        p.writeInt(surfaceHeight);
        if (parameter != null) {
            p.writeFloat(parameter.degrees);
            p.writeFloat(parameter.scaleX);
            p.writeFloat(parameter.scaleY);
            p.writeInt(parameter.cropX);
            p.writeInt(parameter.cropY );
            p.writeInt(parameter.cropWidth );
            p.writeInt(parameter.cropHeight );
        } else {
            p.writeFloat(0);
            p.writeFloat(1);
            p.writeFloat(1);
            p.writeInt(0);
            p.writeInt(0);
            p.writeInt(0);
            p.writeInt(0);
        }

        boolean ret = _setParameter(KEY_PARAMETER_SET_IMAGE_SAMPLESIZE_SURFACESIZE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param
     * @return {@hide}
     * @deprecated This method should not be used.
     */
    @Deprecated
    public boolean ImageRotate(float degrees) {
        Parcel p = Parcel.obtain();
        p.writeFloat(degrees);
        boolean ret = _setParameter(KEY_PARAMETER_ROTATE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param degrees is the rotate degrees.
     * @param isAutoCrop indicates whether or not the automatic cropping.
     * @return true if success, else return false.
     */
    public boolean ImageRotate(float degrees, boolean isAutoCrop) {
        Parcel p = Parcel.obtain();
        p.writeFloat(degrees);
        p.writeInt((int) (isAutoCrop ? 1 : 0));
        boolean ret = _setParameter(KEY_PARAMETER_ROTATE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param
     * @return {@hide}
     * @deprecated This method should not be used.
     */
    @Deprecated
    public boolean ImageScale(float scaleX, float scaleY) {
        Parcel p = Parcel.obtain();
        p.writeFloat(scaleX);
        p.writeFloat(scaleY);
        boolean ret = _setParameter(KEY_PARAMETER_SCALE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param scaleX the X-axis scaling factor.
     * @param scaleY the Y-axis scaling factor.
     * @param isAutoCrop indicates whether or not the automatic cropping.
     * @return true if success, else return false.
     */
    public boolean ImageScale(float scaleX, float scaleY, boolean isAutoCrop) {
        Parcel p = Parcel.obtain();
        p.writeFloat(scaleX);
        p.writeFloat(scaleY);
        p.writeInt((int) (isAutoCrop ? 1 : 0));
        boolean ret = _setParameter(KEY_PARAMETER_SCALE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param degrees is the rotate degrees.
     * @param scaleX the X-axis scaling factor.
     * @param scaleY the Y-axis scaling factor.
     * @param isAutoCrop indicates whether or not the automatic cropping.
     * @return true if success, else return false.
     */
    public boolean ImageRotateAndScale(float degrees, float scaleX, float scaleY, boolean isAutoCrop) {
        Parcel p = Parcel.obtain();
        p.writeFloat(degrees);
        p.writeFloat(scaleX);
        p.writeFloat(scaleY);
        p.writeInt((int) (isAutoCrop ? 1 : 0));
        boolean ret = _setParameter(KEY_PARAMETER_ROTATE_SCALE, p);
        p.recycle();
        return ret;
    }

    /**
     * @param cropX (cropX, cropY) is coordinate of rectangle origin.
     * @param cropWidth is width of the rectangle.
     * @param cropHeight is height of the rectangle.
     * @return true if success, else return false.
     */
    public boolean ImageCropRect(int cropX, int cropY, int cropWidth, int cropHeight) {
        Parcel p = Parcel.obtain();
        p.writeInt(cropX);
        p.writeInt(cropY);
        p.writeInt(cropWidth);
        p.writeInt(cropHeight);
        boolean ret = _setParameter(KEY_PARAMETER_CROP_RECT, p);
        p.recycle();
        return ret;
    }

    /**
     * @param path is the path of photo file.
     * @param imageSampleSize is the number of pixels in either dimension that correspond to a single
     * pixel in the decoded bitmap. For example, imageSampleSize == 4 returns
     * an image that is 1/4 the width/height of the original, and 1/16 the
     * number of pixels. Any value <= 1 is treated the same as 1.
     * @param surfaceWidth is the width of surface.
     * @param surfaceHeight is the height of surface.
     * @param parameter provide more parameters for first show.
     * @return true if success, else return false.
     * @deprecated Use {@link ImageDecodeNext(String, int, int, int, InitParameter, int)}.
     */
    @Deprecated
    public boolean ImageDecodeNext(String path, int imageSampleSize, int surfaceWidth, int surfaceHeight, InitParameter parameter) {
        return ImageDecodeNext(path, imageSampleSize, surfaceWidth, surfaceHeight, parameter, 0);
    }

    /**
     * @param path is the path of photo file.
     * @param imageSampleSize is the number of pixels in either dimension that correspond to a single
     * pixel in the decoded bitmap. For example, imageSampleSize == 4 returns
     * an image that is 1/4 the width/height of the original, and 1/16 the
     * number of pixels. Any value <= 1 is treated the same as 1.
     * @param surfaceWidth is the width of surface.
     * @param surfaceHeight is the height of surface.
     * @param parameter provide more parameters for first show.
     * @param index the index of the photo decoded. So far,only support 0 or 1.
     * @return true if success, else return false.
     */
    public boolean ImageDecodeNext(String path, int imageSampleSize, int surfaceWidth, int surfaceHeight, InitParameter parameter, int index) {
        Parcel p = Parcel.obtain();
        p.writeInt(FileInfo.StringPath.ordinal());
        p.writeString(path);
        p.writeInt(imageSampleSize);
        p.writeInt(surfaceWidth);
        p.writeInt(surfaceHeight);
        if (parameter != null) {
            p.writeFloat(parameter.degrees);
            p.writeFloat(parameter.scaleX);
            p.writeFloat(parameter.scaleY);
            p.writeInt(parameter.cropX);
            p.writeInt(parameter.cropY );
            p.writeInt(parameter.cropWidth );
            p.writeInt(parameter.cropHeight );
        } else {
            p.writeFloat(0);
            p.writeFloat(1);
            p.writeFloat(1);
            p.writeInt(0);
            p.writeInt(0);
            p.writeInt(0);
            p.writeInt(0);
        }
        p.writeInt(index);
        p.writeStrongBinder((new MediaHTTPService(null)).asBinder());
        boolean ret = _setParameter(KEY_PARAMETER_DECODE_NEXT, p);
        p.recycle();
        return ret;
    }

    /**
     * @param fd is the file descriptor of photo file.
     * @param imageSampleSize is the number of pixels in either dimension that correspond to a single
     * pixel in the decoded bitmap. For example, imageSampleSize == 4 returns
     * an image that is 1/4 the width/height of the original, and 1/16 the
     * number of pixels. Any value <= 1 is treated the same as 1.
     * @param surfaceWidth is the width of surface.
     * @param surfaceHeight is the height of surface.
     * @param parameter provide more parameters for first show.
     * @return true if success, else return false.
     * @deprecated Use {@link ImageDecodeNext(FileDescriptor, int, int, int, InitParameter, int)}.
     */
    @Deprecated
    public boolean ImageDecodeNext(FileDescriptor fd, int imageSampleSize, int surfaceWidth, int surfaceHeight, InitParameter parameter) {
        return ImageDecodeNext(fd, imageSampleSize, surfaceWidth, surfaceHeight, parameter, 0);
    }

    /**
     * @param fd is the file descriptor of photo file.
     * @param imageSampleSize is the number of pixels in either dimension that correspond to a single
     * pixel in the decoded bitmap. For example, imageSampleSize == 4 returns
     * an image that is 1/4 the width/height of the original, and 1/16 the
     * number of pixels. Any value <= 1 is treated the same as 1.
     * @param surfaceWidth is the width of surface.
     * @param surfaceHeight is the height of surface.
     * @param parameter provide more parameters for first show.
     * @param index the index of the photo decoded. So far,only support 0 or 1.
     * @return true if success, else return false.
     */
    public boolean ImageDecodeNext(FileDescriptor fd, int imageSampleSize, int surfaceWidth, int surfaceHeight, InitParameter parameter, int index) {
        Parcel p = Parcel.obtain();
        p.writeInt(FileInfo.FileDes.ordinal());
        p.writeFileDescriptor(fd);
        p.writeInt(imageSampleSize);
        p.writeInt(surfaceWidth);
        p.writeInt(surfaceHeight);
        if (parameter != null) {
            p.writeFloat(parameter.degrees);
            p.writeFloat(parameter.scaleX);
            p.writeFloat(parameter.scaleY);
            p.writeInt(parameter.cropX);
            p.writeInt(parameter.cropY );
            p.writeInt(parameter.cropWidth );
            p.writeInt(parameter.cropHeight );
        } else {
            p.writeFloat(0);
            p.writeFloat(1);
            p.writeFloat(1);
            p.writeInt(0);
            p.writeInt(0);
            p.writeInt(0);
            p.writeInt(0);
        }
        p.writeInt(index);

        boolean ret = _setParameter(KEY_PARAMETER_DECODE_NEXT, p);
        p.recycle();
        return ret;
    }

    /**
     * @param
     * @return true if success, else return false.
     * @deprecated Use {@link ImageShowNext(int)}.
     */
    @Deprecated
    public boolean ImageShowNext() {
        return ImageShowNext(0);
    }

    /**
     * @param index the index of the photo you want to show. So far,only support 0 or 1.
     * @return true if success, else return false.
     */
    public boolean ImageShowNext(int index) {
        boolean ret = setParameter(KEY_PARAMETER_SHOW_NEXT, index);
        return ret;
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean DemuxReset() {
        return setParameter(KEY_PARAMETER_DEMUX_RESET, -1);
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean DataSourceSwitch(String path) {
        return setParameter(KEY_PARAMETER_DATASOURCE_SWITCH, path);
    }

    public native void native_setExternalDataSourceInfo(Parcel value);

    public enum EN_MS_DATASOURCE_PLAYER_TYPE {
        E_DATASOURCE_PLAYER_UNKNOW, E_DATASOURCE_PLAYER_MOVIE, E_DATASOURCE_PLAYER_MUSIC,
    }

    public enum EN_MS_DATASOURCE_CONTENT_TYPE {
        E_DATASOURCE_CONTENT_TYPE_MASS_STORAGE, E_DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITH_SEEK, E_DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITHOUT_SEEK, E_DATASOURCE_CONTENT_TYPE_ES,
    }

    public enum EN_MS_DATASOURCE_ES_VIDEO_CODEC {
        // / Video codec type is unknow.
        E_DATASOURCE_ES_VIDEO_CODEC_UNKNOW,
        // / Video codec type is MPEG 1.
        E_DATASOURCE_ES_VIDEO_CODEC_MPEG1VIDEO,
        // / Video codec type is MPEG 2.
        E_DATASOURCE_ES_VIDEO_CODEC_MPEG2VIDEO,
        // / Video codec type is MPEG 4.
        E_DATASOURCE_ES_VIDEO_CODEC_MPEG4,
        // / Video codec type is H263.
        E_DATASOURCE_ES_VIDEO_CODEC_H263,
        // / Video codec type is DIVX3.
        E_DATASOURCE_ES_VIDEO_CODEC_DIVX3,
        // / Video codec type is DIVX4.
        E_DATASOURCE_ES_VIDEO_CODEC_DIVX4,
        // / Video codec type is DIVX.
        E_DATASOURCE_ES_VIDEO_CODEC_DIVX,
        // / Video codec type is H264.
        E_DATASOURCE_ES_VIDEO_CODEC_H264,
        // / Video codec type is Advance H264.
        E_DATASOURCE_ES_VIDEO_CODEC_AVS,
        // / Video codec type is RM V3.0.
        E_DATASOURCE_ES_VIDEO_CODEC_RV30,
        // / Video codec type is RM V4.0.
        E_DATASOURCE_ES_VIDEO_CODEC_RV40,
        // / Video codec type is motion JPG.
        E_DATASOURCE_ES_VIDEO_CODEC_MJPEG,
        // / Video codec type is VC1.
        E_DATASOURCE_ES_VIDEO_CODEC_VC1,
        // / Video codec type is WMV 3.0.
        E_DATASOURCE_ES_VIDEO_CODEC_WMV3,
        // / Video codec type is FLV.
        E_DATASOURCE_ES_VIDEO_CODEC_FLV,
        // / Video codec type is FOURCC-EX.
        E_DATASOURCE_ES_VIDEO_CODEC_FOURCCEX,
        // / Video codec type is TS.
        E_DATASOURCE_ES_VIDEO_CODEC_TS,
    }

    public enum EN_MS_DATASOURCE_ES_AUDIO_CODEC {
        // / Audio codec type is none.
        E_DATASOURCE_ES_AUDIO_CODEC_UNKNOW,
        // / Audio codec type is WMA.
        E_DATASOURCE_ES_AUDIO_CODEC_WMA,
        // / Audio codec type is DTS.
        E_DATASOURCE_ES_AUDIO_CODEC_DTS,
        // / Audio codec type is MP3.
        E_DATASOURCE_ES_AUDIO_CODEC_MP3,
        // / Audio codec type is MPEG.
        E_DATASOURCE_ES_AUDIO_CODEC_MPEG,
        // / Audio codec type is AC3.
        E_DATASOURCE_ES_AUDIO_CODEC_AC3,
        // / Audio codec type is AC3 plus.
        E_DATASOURCE_ES_AUDIO_CODEC_AC3_PLUS,
        // / Audio codec type is AAC.
        E_DATASOURCE_ES_AUDIO_CODEC_AAC,
        // / Audio codec type is PCM.
        E_DATASOURCE_ES_AUDIO_CODEC_PCM,
        // / Audio codec type is ADPCM
        E_DATASOURCE_ES_AUDIO_CODEC_ADPCM,
        // / Audio codec type is RAAC.
        E_DATASOURCE_ES_AUDIO_CODEC_RAAC,
        // / Audio codec type is COOK.
        E_DATASOURCE_ES_AUDIO_CODEC_COOK,
        // / Audio codec type is FLAC.
        E_DATASOURCE_ES_AUDIO_CODEC_FLAC,
        // / Audio codec type is VORBIS.
        E_DATASOURCE_ES_AUDIO_CODEC_VORBIS,
        // / Audio codec type is AMR NB.
        E_DATASOURCE_ES_AUDIO_CODEC_AMR_NB,
        // / Audio codec type is AMR WB.
        E_DATASOURCE_ES_AUDIO_CODEC_AMR_WB
    }

    public enum EN_DATASOURCE_MEDIA_FORMAT_TYPE {
        // / Media format type unknown
        E_DATASOURCE_MEDIA_FORMAT_TYPE_UNKNOWN,
        // / Media format type avi
        E_DATASOURCE_MEDIA_FORMAT_TYPE_AVI,
        // / Media format type mp4
        E_DATASOURCE_MEDIA_FORMAT_TYPE_MP4,
        // / Media format type mkv
        E_DATASOURCE_MEDIA_FORMAT_TYPE_MKV,
        // / Media format type asf
        E_DATASOURCE_MEDIA_FORMAT_TYPE_ASF,
        // / Media format type rm
        E_DATASOURCE_MEDIA_FORMAT_TYPE_RM,
        // / Media format type ts
        E_DATASOURCE_MEDIA_FORMAT_TYPE_TS,
        // / Media format type mpg
        E_DATASOURCE_MEDIA_FORMAT_TYPE_MPG,
        // / Media format type flv
        E_DATASOURCE_MEDIA_FORMAT_TYPE_FLV,
        // / Media format type esdata
        E_DATASOURCE_MEDIA_FORMAT_TYPE_ESDATA,
        // / Media format type max
        E_DATASOURCE_MEDIA_FORMAT_TYPE_MAX
    }

    public enum EN_DATASOURCE_APP_TYPE {
        // / Normal APP
        E_DATASOURCE_AP_NORMAL,
        // / Netflix
        E_DATASOURCE_AP_NETFLIX,
        // / DLNA
        E_DATASOURCE_AP_DLNA,
        // / HBBTV
        E_DATASOURCE_AP_HBBTV,
        // / WEBBROWSER
        E_DATASOURCE_AP_WEBBROWSER,
        // / WMDRM10
        E_DATASOURCE_AP_WMDRM10,
        // / Android USB
        E_DATASOURCE_AP_ANDROID_USB,
        // / Android Streaming
        E_DATASOURCE_AP_ANDROID_STREAMING
    }

    // / Define movie format.
    public enum EN_MOVIE_THUMBNAIL_FORMAT {
        // / movie thumbnail format is ARGB8888.
        E_MOVIE_THUMBNAIL_ARGB8888,
        // / MOVIE thumbnail format is ARGB1555.
        E_MOVIE_THUMBNAIL_ARGB1555,
        // / Photo format is ARGB4444.
        E_MOVIE_THUMBNAIL_ARGB4444,
        // / MOVIE thumbnail format is YUV422
        E_MOVIE_THUMBNAIL_YUV422,
        // / MOVIE thumbnail format is none.
        E_MOVIE_THUMBNAIL_NOT_SUPPORT
    }

    private static final int MS_DATASOURCE_PLAYER_UNKNOW = 0;

    private static final int MS_DATASOURCE_PLAYER_MOVIE = 1;

    private static final int MS_DATASOURCE_PLAYER_MUSIC = 2;

    private static final int DATASOURCE_CONTENT_TYPE_MASS_STORAGE = 0;

    private static final int DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITH_SEEK = 1;

    private static final int DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITHOUT_SEEK = 2;

    private static final int DATASOURCE_CONTENT_TYPE_ES = 3;

    private static final int DATASOURCE_ES_VIDEO_CODEC_UNKNOW = -1;

    private static final int DATASOURCE_ES_VIDEO_CODEC_MPEG1VIDEO = 0;

    private static final int DATASOURCE_ES_VIDEO_CODEC_MPEG2VIDEO = 1;

    private static final int DATASOURCE_ES_VIDEO_CODEC_MPEG4 = 2;

    private static final int DATASOURCE_ES_VIDEO_CODEC_H263 = 3;

    private static final int DATASOURCE_ES_VIDEO_CODEC_DIVX3 = 4;

    private static final int DATASOURCE_ES_VIDEO_CODEC_DIVX4 = 5;

    private static final int DATASOURCE_ES_VIDEO_CODEC_DIVX = 6;

    private static final int DATASOURCE_ES_VIDEO_CODEC_H264 = 7;

    private static final int DATASOURCE_ES_VIDEO_CODEC_AVS = 8;

    private static final int DATASOURCE_ES_VIDEO_CODEC_RV30 = 9;

    private static final int DATASOURCE_ES_VIDEO_CODEC_RV40 = 10;

    private static final int DATASOURCE_ES_VIDEO_CODEC_MJPEG = 11;

    private static final int DATASOURCE_ES_VIDEO_CODEC_VC1 = 12;

    private static final int DATASOURCE_ES_VIDEO_CODEC_WMV3 = 13;

    private static final int DATASOURCE_ES_VIDEO_CODEC_FLV = 14;

    private static final int DATASOURCE_ES_VIDEO_CODEC_FOURCCEX = 15;

    private static final int DATASOURCE_ES_VIDEO_CODEC_TS = 16;

    private static final int DATASOURCE_ES_AUDIO_CODEC_UNKNOW = -1;

    private static final int DATASOURCE_ES_AUDIO_CODEC_WMA = 0;

    private static final int DATASOURCE_ES_AUDIO_CODEC_DTS = 1;

    private static final int DATASOURCE_ES_AUDIO_CODEC_MP3 = 2;

    private static final int DATASOURCE_ES_AUDIO_CODEC_MPEG = 3;

    private static final int DATASOURCE_ES_AUDIO_CODEC_AC3 = 4;

    private static final int DATASOURCE_ES_AUDIO_CODEC_AC3_PLUS = 5;

    private static final int DATASOURCE_ES_AUDIO_CODEC_AAC = 6;

    private static final int DATASOURCE_ES_AUDIO_CODEC_PCM = 7;

    private static final int DATASOURCE_ES_AUDIO_CODEC_ADPCM = 8;

    private static final int DATASOURCE_ES_AUDIO_CODEC_RAAC = 9;

    private static final int DATASOURCE_ES_AUDIO_CODEC_COOK = 10;

    private static final int DATASOURCE_ES_AUDIO_CODEC_FLAC = 11;

    private static final int DATASOURCE_ES_AUDIO_CODEC_VORBIS = 12;

    private static final int DATASOURCE_ES_AUDIO_CODEC_AMR_NB = 13;

    private static final int DATASOURCE_ES_AUDIO_CODEC_AMR_WB = 14;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_UNKNOWN = -1;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_AVI = 0;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_MP4 = 1;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_MKV = 2;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_ASF = 3;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_RM = 4;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_TS = 5;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_MPG = 6;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_FLV = 7;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_ESDATA = 8;

    private static final int DATASOURCE_MEDIA_FORMAT_TYPE_MAX = 9;

    private static final int DATASOURCE_AP_NORMAL = 0;

    private static final int DATASOURCE_AP_NETFLIX = 1;

    private static final int DATASOURCE_AP_DLNA = 2;

    private static final int DATASOURCE_AP_HBBTV = 3;

    private static final int DATASOURCE_AP_WEBBROWSER = 4;

    private static final int DATASOURCE_AP_WMDRM10 = 5;

    private static final int DATASOURCE_AP_ANDROID_USB = 6;

    private static final int DATASOURCE_AP_ANDROID_STREAMING = 7;

    private int mDataSourcePlayerType = 0;

    private int mDataSourceContentType = 0;

    private int mDataSourceVideoCodec = 0;

    private int mDataSourceAudioCodec = 0;

    private int mDataSourceMediaFormat = 0;

    private int mDataSourceAppType = 0;

    /**
     * @param
     * @return {@hide}
     */
    public boolean setExternalDataSourceAppType(EN_DATASOURCE_APP_TYPE type) {
        switch (type) {
            case E_DATASOURCE_AP_NETFLIX:
                mDataSourceAppType = DATASOURCE_AP_NETFLIX;
                break;
            case E_DATASOURCE_AP_DLNA:
                mDataSourceAppType = DATASOURCE_AP_DLNA;
                break;
            case E_DATASOURCE_AP_HBBTV:
                mDataSourceAppType = DATASOURCE_AP_HBBTV;
                break;
            case E_DATASOURCE_AP_WEBBROWSER:
                mDataSourceAppType = DATASOURCE_AP_WEBBROWSER;
                break;
            case E_DATASOURCE_AP_WMDRM10:
                mDataSourceAppType = DATASOURCE_AP_WMDRM10;
                break;
            case E_DATASOURCE_AP_ANDROID_USB:
                mDataSourceAppType = DATASOURCE_AP_ANDROID_USB;
                break;
            case E_DATASOURCE_AP_ANDROID_STREAMING:
                mDataSourceAppType = DATASOURCE_AP_ANDROID_STREAMING;
                break;
            default:
                mDataSourceAppType = DATASOURCE_AP_NORMAL;
                break;
        }
        return true;
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean setExternalDataSourceMediaFormat(EN_DATASOURCE_MEDIA_FORMAT_TYPE type) {
        switch (type) {
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_AVI:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_AVI;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_MP4:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_MP4;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_MKV:
                mDataSourceMediaFormat = DATASOURCE_ES_VIDEO_CODEC_MPEG4;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_ASF:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_ASF;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_RM:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_RM;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_TS:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_TS;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_MPG:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_MPG;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_FLV:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_FLV;
                break;
            case E_DATASOURCE_MEDIA_FORMAT_TYPE_ESDATA:
                mDataSourceMediaFormat = DATASOURCE_MEDIA_FORMAT_TYPE_ESDATA;
                break;
            default:
                mDataSourceMediaFormat = DATASOURCE_ES_VIDEO_CODEC_UNKNOW;
                break;
        }
        return true;
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean setExternalDataSourceVideoCodec(EN_MS_DATASOURCE_ES_VIDEO_CODEC type) {
        switch (type) {
            case E_DATASOURCE_ES_VIDEO_CODEC_MPEG1VIDEO:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_MPEG1VIDEO;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_MPEG2VIDEO:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_MPEG2VIDEO;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_MPEG4:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_MPEG4;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_H263:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_H263;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_DIVX3:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_DIVX3;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_DIVX4:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_DIVX4;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_DIVX:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_DIVX;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_H264:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_H264;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_AVS:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_AVS;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_RV30:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_RV30;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_RV40:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_RV40;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_MJPEG:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_MJPEG;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_VC1:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_VC1;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_WMV3:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_WMV3;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_FLV:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_FLV;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_FOURCCEX:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_FOURCCEX;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_TS:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_TS;
                break;
            case E_DATASOURCE_ES_VIDEO_CODEC_UNKNOW:
            default:
                mDataSourceVideoCodec = DATASOURCE_ES_VIDEO_CODEC_UNKNOW;
                break;
        }
        return true;
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean setExternalDataSourceAudioCodec(EN_MS_DATASOURCE_ES_AUDIO_CODEC type) {
        switch (type) {
            case E_DATASOURCE_ES_AUDIO_CODEC_WMA:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_WMA;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_DTS:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_DTS;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_MP3:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_MP3;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_MPEG:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_MPEG;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_AC3:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_AC3;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_AC3_PLUS:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_AC3_PLUS;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_AAC:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_AAC;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_PCM:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_PCM;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_ADPCM:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_ADPCM;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_RAAC:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_RAAC;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_COOK:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_COOK;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_FLAC:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_FLAC;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_VORBIS:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_VORBIS;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_AMR_NB:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_AMR_NB;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_AMR_WB:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_AMR_WB;
                break;
            case E_DATASOURCE_ES_AUDIO_CODEC_UNKNOW:
            default:
                mDataSourceAudioCodec = DATASOURCE_ES_AUDIO_CODEC_UNKNOW;
                break;
        }
        return true;
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean setExternalDataSourcePlayerType(EN_MS_DATASOURCE_PLAYER_TYPE type) {
        switch (type) {
            case E_DATASOURCE_PLAYER_MOVIE:
                mDataSourcePlayerType = MS_DATASOURCE_PLAYER_MOVIE;
                break;
            case E_DATASOURCE_PLAYER_MUSIC:
                mDataSourcePlayerType = MS_DATASOURCE_PLAYER_MUSIC;
                break;
            case E_DATASOURCE_PLAYER_UNKNOW:
            default:
                mDataSourcePlayerType = MS_DATASOURCE_PLAYER_UNKNOW;
                break;
        }
        return true;
    }

    /**
     * @param
     * @return {@hide}
     */
    public boolean setExternalDataSourceContentType(EN_MS_DATASOURCE_CONTENT_TYPE type) {
        switch (type) {
            case E_DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITH_SEEK:
                mDataSourceContentType = DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITH_SEEK;
                break;
            case E_DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITHOUT_SEEK:
                mDataSourceContentType = DATASOURCE_CONTENT_TYPE_NETWORK_STREAM_WITHOUT_SEEK;
                break;
            case E_DATASOURCE_CONTENT_TYPE_ES:
                mDataSourceContentType = DATASOURCE_CONTENT_TYPE_ES;
                break;
            default:
            case E_DATASOURCE_CONTENT_TYPE_MASS_STORAGE:
                mDataSourceContentType = DATASOURCE_CONTENT_TYPE_MASS_STORAGE;
                break;
        }
        return true;
    }

    @Override
    public void release() {
        _release();
        super.release();
    }

    private native void _release();
    /**
     * Sets the data source (external data source) to use.
     *
     * @param dataSource the stream you want to play
     * @throws IllegalStateException if it is called in an invalid state
     */
    public void setDataSource(InputStream dataSource) throws IOException, IllegalArgumentException,
            IllegalStateException {
        Parcel p = Parcel.obtain();
        p.writeInt(mDataSourcePlayerType);
        p.writeInt(mDataSourceContentType);
        p.writeInt(mDataSourceVideoCodec);
        p.writeInt(mDataSourceAudioCodec);
        p.writeInt(mDataSourceMediaFormat);
        p.writeInt(mDataSourceAppType);
        native_setExternalDataSourceInfo(p);
        _setDataSource(dataSource);
        mDataSourcePlayerType = 0;
        mDataSourceContentType = 0;
        mDataSourceVideoCodec = 0;
        mDataSourceAudioCodec = 0;
        mDataSourceMediaFormat = 0;
        mDataSourceAppType = 0;
    }

    private native void _setDataSource(InputStream dataSource) throws IOException,
            IllegalArgumentException, IllegalStateException;

    /**
     * @param
     * @return {@hide}
     */
    public boolean SetSeamlessMode(EnumPlayerSeamlessMode index) {
        int idx = -1;

        switch (index) {
            case E_PLAYER_SEAMLESS_NONE:
                idx = 0;
                break;
            case E_PLAYER_SEAMLESS_FREEZ:
                idx = 1;
                break;
            case E_PLAYER_SEAMLESS_SMOTH:
                idx = 2;
                break;
            case E_PLAYER_SEAMLESS_DS:
                idx = 3;
                break;
            default:
                idx = -1;
        }
        if (idx == -1)
            return false;
        return setParameter(KEY_PARAMETER_SET_SEAMLESS_MODE, idx);
    }

    /**
     * A new set of subtitle is available.
     *
     * @see android.media.MediaPlayer.OnInfoListener
     */
    public static final int MEDIA_INFO_SUBTITLE_UPDATA = 1000;

    // Online play cache percentage
    public static final int MEDIA_INFO_NETWORK_CACHE_PERCENT = 1001;

    // Audio format unsupport.
    public static final int MEDIA_INFO_AUDIO_UNSUPPORT = 1002;

    // Video format unsupport.
    public static final int MEDIA_INFO_VIDEO_UNSUPPORT = 1003;

    // Video decoder report continuous error
    public static final int MEDIA_INFO_VIDEO_DECODE_CONTINUOUS_ERR = 1004;

    // Video display by hardware
    public static final int MEDIA_INFO_VIDEO_DISPLAY_BY_HARDWARE = 1005;

    // Trick play complete notify
    public static final int MEDIA_INFO_TRICK_PLAY_COMPLETE = 1006;

    // Video HDR change. Only notify if HDR on/off is switched.
    public static final int MEDIA_INFO_VIDEO_HDR_CHANGE = 1007;

    // Video decoder exceeds its capability
    public static final int MEDIA_INFO_VIDEO_DECODER_OVER_CAPABILITY = 1008;

    // Video  subtitle unsupport notify
    public static final int MEDIA_INFO_SUBTITLE_UNSUPPORT = 1009;

    // Trick play unsupport
    public static final int MEDIA_INFO_TRICK_PLAY_UNSUPPORT = 1012;

    public static final int MEDIA_INFO_PHOTO_DECODE_NEXT_ERR = 1020;

    public static final int MEDIA_ERROR_BASE = -5000;

    // Online play is connected to server timeout
    public static final int MEDIA_ERROR_CONNECTED_TIMEOUT = MEDIA_ERROR_BASE;

    // The audio format is not supported
    public static final int MEDIA_ERROR_AUDIO_UNSUPPORT = MEDIA_ERROR_BASE - 1;

    // The video format is not supported
    public static final int MEDIA_ERROR_VIDEO_UNSUPPORT = MEDIA_ERROR_BASE - 2;

    // The file format is not supported
    public static final int MEDIA_ERROR_FILE_FORMAT_UNSUPPORT = MEDIA_ERROR_BASE - 3;

    // Out of memory
    public static final int MEDIA_ERROR_OUT_OF_MEMORY = MEDIA_ERROR_BASE - 4;

    // The video continuous skip frames too much
    public static final int MEDIA_ERROR_VIDEO_SKIP_TOO_MUCH = MEDIA_ERROR_BASE - 5;

    // The video resource had been stolen
    public static final int MEDIA_ERROR_VIDEO_RESOURCE_LOST = MEDIA_ERROR_BASE - 6;

    // Divx DRM video reltal expire
    public static final int MEDIA_ERROR_DIVX_DRM_RENTAL_EXPIRE = MEDIA_ERROR_BASE - 7;

    // Divx DRM authentication fail
    public static final int MEDIA_ERROR_DIVX_DRM_AUTH_FAIL = MEDIA_ERROR_BASE - 8;

    /**
     * Convenience method to create a MediaPlayer for a given Uri. On success,
     * {@link #prepare()} will already have been called and must not be called
     * again.
     * <p>
     * When done with the MediaPlayer, you should call {@link #release()}, to
     * free the resources. If not released, too many MediaPlayer instances will
     * result in an exception.
     * </p>
     *
     * @param inputStream the stream from which to get the datasource
     * @param holder the SurfaceHolder to use for displaying the video
     * @return a MediaPlayer object, or null if creation failed
     */
    public static MMediaPlayer create(InputStream inputStream, SurfaceHolder holder) {
        MMediaPlayer mp = null;
        try {
            mp = new MMediaPlayer();
            mp.setDataSource(inputStream);
            if (holder != null) {
                mp.setDisplay(holder);
            }
            mp.prepare();
            return mp;
        } catch (IOException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        } catch (SecurityException ex) {
            Log.d(TAG, "create failed:", ex);
            // fall through
        } finally {
            if (mp != null) {
                mp.release();
                mp = null;
            }
        }

        return null;
    }

    /**
     * To get the string from mstarplayer for audiotrack
     *
     * @param infoType 1:title;2:album;3:artist; {@hide}
     */
    public native String getAudioTrackStringData(int infoType);

    /**
     * To support extended features in the STAOS,two API are added. Gets audio
     * track information using the Audio TrackInfo structure.
     *
     * @param typeIsAudio controls whether the get the audio information or get
     *            the vedio information.See ture:type is audio;false:type is
     *            video
     * @return The Parcel, possibly empty. null if an error occured.
     */
    public AudioTrackInfo getAudioTrackInfo(final boolean typeIsAudio) {
        AudioTrackInfo info = null;
        Parcel reply = null;
        try {
            reply = getParcelParameter(KEY_PARAMETER_GET_AUDIO_TRACK_INFO);

            String strInfo[] = new String[3];
            if (typeIsAudio == true) {
                for (int i = 0; i < 3; i++) {
                    strInfo[i] = getAudioTrackStringData(i);
                }
            }

            if (reply != null) {
                info = new AudioTrackInfo(typeIsAudio, reply, strInfo);
            } else {
                Log.e(TAG, "getAudioTrackInfo: getParameter KEY_PARAMETER_GET_AUDIO_TRACK_INFO return null!!");
            }
        } catch (Exception e) {
            Log.e(TAG, "getAudioTrackInfo: getParameter KEY_PARAMETER_GET_AUDIO_TRACK_INFO return Exception:", e);
        } finally {
            if (reply != null) {
                reply.recycle();
            }
        }

        return info;
    }

    /**
     * @param trackIndex the track id which you want to get information
     * @return The metadata, possibly empty. null if an error occured.
     */
    public SubtitleTrackInfo getSubtitleTrackInfo(final int trackIndex) {
        if (setParameter(KEY_PARAMETER_SET_SUBTITLE_INDEX, trackIndex)) {
            Parcel reply = getParcelParameter(KEY_PARAMETER_GET_SUBTITLE_TRACK_INFO);
            SubtitleTrackInfo info = new SubtitleTrackInfo(reply);
            reply.recycle();
            return info;
        } else {
            return null;
        }
    }

    /**
     * Gets all of the subtitletracks' information.
     *
     * @return The metadata, possibly empty. null if an error occured.
     */
    public SubtitleTrackInfo getAllSubtitleTrackInfo() {
        Parcel reply = getParcelParameter(KEY_PARAMETER_GET_ALL_SUBTITLE_TRACK_INFO);
        SubtitleTrackInfo info = new SubtitleTrackInfo(reply, 0);
        reply.recycle();
        return info;
    }

    /**
     * Set audio track with track number that you want to play.
     *
     * @param track track number that you want to play. Note:Before using this
     *            interface, the caller should call
     *            {@link #getMetadata(boolean,boolean)} firstly to get Metadata
     *            and then get totalTrackNum from it.The param track must be
     *            setted among 0 and totalTrackNum-1.
     */
    public native void setAudioTrack(int track);

    /**
     * Set the subtitle track with track number that you want to play.
     *
     * @param track number that you want to play
     */
    public native void setSubtitleTrack(int track);

    /**
     * Set the Content URI of the data you want to play.
     *
     * @param Uri the Content URI of the data you want to play.
     */
    public native void setSubtitleDataSource(String Uri);

    /**
     * set PlayMode
     *
     * @param speed the forward/backword speed which is -32, -16, -8, -4, -2, *
     *            2, 4, 8, 16, 32.
     * @return True if set PlayMode is OK, else false.
     */
    public native boolean setPlayMode(int speed);

    /**
     * get setPlayMode,
     *
     * @return speed information for application Layer: -32, -16, -8, -4, -2, *
     *         2, 4, 8, 16, 32.
     */
    public native int getPlayMode();

    /**
     * set PlayModePermille
     *
     * @param speed the forward/backword speed. 1000 means 1X.
     * @return True if set PlayModePermille is OK, else false.
     */
    public native int setPlayModePermille(int speed_permille);

    /**
     * get setPlayModePermille
     *
     * @return speed information for application Layer: 1000 means 1X.
     */
    public native int getPlayModePermille();

    /**
     * turn on the subtitle.
     */
    public native void onSubtitleTrack();

    /**
     * turn off the subtitle.
     */
    public native void offSubtitleTrack();

    /**
     * Get subtitle text data
     *
     * @return return the subtitle text data
     */
    public native String getSubtitleData();

    private native void _setSubtitleSurface(Surface surface);

    private void updateSubtitleSurfaceScreenOn() {
        if (mSubtitleSurfaceHolder != null) {
            mSubtitleSurfaceHolder.setKeepScreenOn(false);
        }
    }

    /**
     * Sets the SurfaceHolder to use for displaying the subtitle portion of the
     * media.
     */
    public void setSubtitleDisplay(SurfaceHolder sh) {

        mSubtitleSurfaceHolder = sh;
        Surface surface;
        if (sh != null) {
            surface = sh.getSurface();
        } else {
            surface = null;
        }
        _setSubtitleSurface(surface);
        updateSubtitleSurfaceScreenOn();
    }

    /**
     * Class for MMediaPlayer to return each audio/video/subtitle track's metadata.
     *
     * @see android.media.MMediaPlayer#getMsTrackInfo
     */
    static public class MsTrackInfo implements Parcelable {
        /**
         * Gets the track type.
         * @return TrackType which indicates if the track is video, audio, timed text, timed bitmap.
         */
        public int getTrackType() {
            return mTrackType;
        }

        /**
         * Gets the language code of the track.
         * @return a language code in either way of ISO-639-1 or ISO-639-2.
         * When the language is unknown or could not be determined,
         * ISO-639-2 language code, "und", is returned.
         */
        public String getLanguage() {
            return mLanguage;
        }

        /**
        * Gets the PID.
        */
        public int getStreamId() {
            return mStreamId;
        }

        /**
        * Gets the mime type
        */
        public String getMimeType() {
            return mMimeType;
        }

        public static final int MEDIA_TRACK_TYPE_UNKNOWN = 0;
        public static final int MEDIA_TRACK_TYPE_VIDEO = 1;
        public static final int MEDIA_TRACK_TYPE_AUDIO = 2;
        public static final int MEDIA_TRACK_TYPE_TIMEDTEXT = 3;
        public static final int MEDIA_TRACK_TYPE_SUBTITLE = 4;
        public static final int MEDIA_TRACK_TYPE_TIMEDBITMAP = 5;

        final int mTrackType;
        final String mMimeType;
        final String mLanguage;
        final int mStreamId;

        MsTrackInfo(Parcel in) {
            mTrackType = in.readInt();
            mMimeType = in.readString();
            mLanguage = in.readString();
            mStreamId = in.readInt();
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public int describeContents() {
            return 0;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(mTrackType);
            dest.writeString(mMimeType);
            dest.writeString(mLanguage);
            dest.writeInt(mStreamId);
        }

        /**
         * Used to read a MsTrackInfo from a Parcel.
         */
        static final Parcelable.Creator<MsTrackInfo> CREATOR
                = new Parcelable.Creator<MsTrackInfo>() {
                    @Override
                    public MsTrackInfo createFromParcel(Parcel in) {
                        return new MsTrackInfo(in);
                    }

                    @Override
                    public MsTrackInfo[] newArray(int size) {
                        return new MsTrackInfo[size];
                    }
                };

    };

    /**
     * Returns an array of track information.
     *
     * @return Array of track info. The total number of tracks is the array length.
     * Must be called again if an external timed text source has been added after any of the
     * addTimedTextSource methods are called.
     * @throws IllegalStateException if it is called in an invalid state.
     */
    public MsTrackInfo[] getMsTrackInfo() throws IllegalStateException {
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            request.writeInterfaceToken(IMEDIA_PLAYER);
            request.writeInt(INVOKE_ID_GET_MS_TRACK_INFO);
            invoke(request, reply);
            MsTrackInfo trackInfo[] = reply.createTypedArray(MsTrackInfo.CREATOR);
            return trackInfo;
        } finally {
            request.recycle();
            reply.recycle();
        }
    }

    private static native final void native_init();

    public native int setSubtitleSync(int timeMs);

    public native void divx_SetTitle(int u32ID);

    public native int divx_GetTitle();

    public native void divx_SetEdition(int u32ID);

    public native int divx_GetEdition();

    public native void divx_SetChapter(int u32ID);

    public native int divx_GetChapter();

    public native void divx_SetAutochapter(int u32ID);

    public native int divx_GetAutochapter();

    public native int divx_GetAutochapterTime(int u32ID);

    public native int divx_GetChapterTime(int u32ID);

    public native int native_divx_SetResumePlay(Parcel inResumeInfo);

    public native void native_setVideoDisplayAspectRatio(int eRatio);

    public native Bitmap native_captureMovieThumbnail(Parcel pIn, Parcel pOut);

    public native void native_divx_SetReplayFlag(boolean isResumePlay);

    public DivxDrmInfo divx_GetDrmInfo() {
        DivxDrmInfo info = null;
        Parcel reply = null;
        try {
            reply = getParcelParameter(KEY_PARAMETER_GET_DIVX_DRM_INFO);
            if (reply!= null) {
                info = new DivxDrmInfo(reply);
                reply.recycle();
            } else {
                Log.e(TAG, "divx_GetDrmInfo getMetadata return null!!");
            }
        } catch (Exception e) {
            Log.e(TAG, "getAudioTrackInfo: getParameter KEY_PARAMETER_GET_DIVX_DRM_INFO return Exception:", e);
        } finally {
            if (reply != null) {
                reply.recycle();
            }
        }
        return info;
    }

    public int divx_SetResumePlay(DivxResumeInfo info) {
        Parcel p = Parcel.obtain();
        p.writeLong(info.getFilePos());
        p.writeInt(info.getPTS());
        p.writeInt(info.getResumeMKV());
        p.writeInt(info.getTitle());
        p.writeInt(info.getEdition());
        int ret = native_divx_SetResumePlay(p);
        p.recycle();
        return ret;
    }

    public void divx_GetResumePlay(DivxResumeInfo info) {
        Parcel reply = null;
        try {
            reply = getParcelParameter(KEY_PARAMETER_GET_DIVX_RESUME_PLAY);
            if (reply != null) {
                long divxFilePos = reply.readLong();
                if (divxFilePos != -1) {
                    info.setFilePos(divxFilePos);
                }

                int divxPts = reply.readInt();
                if (divxPts != -1) {
                    info.setPTS(divxPts);
                }

                int divxResumeMkv = reply.readInt();
                if (divxResumeMkv != -1) {
                    info.setResumeMKV(divxResumeMkv);
                }

                int divxTitle = reply.readInt();
                if (divxTitle != -1) {
                    info.setTitle(divxTitle);
                }

                int divxEdition = reply.readInt();
                if (divxEdition != -1) {
                    info.setEdition(divxEdition);
                }

                reply.recycle();
            } else {
                Log.e(TAG, "divx_GetResumePlay getMetadata return null!!");
            }
        } catch (Exception e) {
            Log.e(TAG, "getAudioTrackInfo: getParameter KEY_PARAMETER_GET_DIVX_RESUME_PLAY return Exception:", e);
        } finally {
            if (reply != null) {
                reply.recycle();
            }
        }
    }

    public void divx_GetDrmRegInfo(String sPath, String[] sInfo) {
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();

        try {
            retriever.setDataSource(sPath);
            for (int i = 0; i < 4; i++) {
                sInfo[i] = retriever.extractMetadata(METADATA_KEY_DIVX_DRM_IS_DEVICE_FIRSTTIME_REG
                        + i);
            }
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
                // Ignore failures while cleaning up.
            }
        }

    }

    public String getAudioCodecType() {
        String info = null;
        Parcel reply = null;

        String audioIndicator = getAudioIndicator();
        if (audioIndicator != null && audioIndicator.contains("DTS")) {
            return audioIndicator.substring(audioIndicator.indexOf("DTS"));
        }

        try {
            reply = getParcelParameter(KEY_PARAMETER_GET_AUDIO_CODEC_TYPE);
            if (reply != null ) {
                info = reply.readString();
            }

        } catch (Exception e) {
            Log.e(TAG, "getAudioTrackInfo: getParameter KEY_PARAMETER_GET_AUDIO_CODEC_TYPE return Exception:", e);
        } finally {
            if (reply != null) {
                reply.recycle();
            }
        }

        return info;
    }

    public VideoCodecInfo getVideoInfo() {
        Parcel reply = null;
        VideoCodecInfo info = null;
        try {
            reply = getParcelParameter(KEY_PARAMETER_GET_VIDEO_INFO);
            if (reply != null) {
                info = new VideoCodecInfo(reply);
            } else {
                Log.e(TAG, "getVideoInfo getMetadata return null!!");
            }
        } catch (Exception e) {
            Log.e(TAG, "getAudioTrackInfo: getParameter KEY_PARAMETER_GET_VIDEO_INFO return Exception:", e);
        } finally {
            if (reply != null) {
                reply.recycle();
            }
        }
        return info;
    }

    public String getAudioIndicator() {
        Parcel reply = null;
        String audioIndicator = null;
        try {
            reply = getParcelParameter(KEY_PARAMETER_GET_AUDIO_INDICATOR);
            if (reply != null) {
                audioIndicator = reply.readString();
            } else {
                Log.e(TAG, "getAudioIndicator return null!!");
            }
        } catch (Exception e) {
            Log.e(TAG, "getAudioIndicator: getParameter KEY_PARAMETER_GET_AUDIO_INDICATOR return Exception:", e);
        } finally {
            if (reply != null) {
                reply.recycle();
            }
        }
        return audioIndicator;
    }

    public Bitmap captureVideoThumbnail(String filePath, int wdt, int hgt) {
        Bitmap bitmap = null;
        MediaMetadataRetriever retriever = new MediaMetadataRetriever();
        try {
            retriever.setDataSource(filePath);
            bitmap = retriever.getFrameAtTime(-1);
        } catch (IllegalArgumentException ex) {
            // Assume this is a corrupt video file
        } catch (RuntimeException ex) {
            // Assume this is a corrupt video file.
        } finally {
            try {
                retriever.release();
            } catch (RuntimeException ex) {
                // Ignore failures while cleaning up.
            }
        }

        if (bitmap == null)
            return null;

        bitmap = Bitmap.createScaledBitmap(bitmap, wdt, hgt, true);

        return bitmap;
    }

    public void captureVideoThumbnailRelease() {
        Log.d(TAG, "captureVideoThumbnailRelease");
        release();
    }

    // Define video display aspect ratio.
    public enum EnumVideoAspectRatio {
        // / Video display aspect ratio is depended on panel resolution.
        E_VIDEO_ASPECT_RATIO_AUTO,
        // / Video display aspect ratio is 4x3.
        E_VIDEO_ASPECT_RATIO_4X3,
        // / Video display aspect ratio is 16x9.
        E_VIDEO_ASPECT_RATIO_16X9,
        // / Video display aspect ratio is 16x9 pillarbox.
        E_VIDEO_ASPECT_RATIO_16X9_PILLARBOX,
        // / Video display aspect ratio is 4x3 pan scan.
        E_VIDEO_ASPECT_RATIO_4X3_PAN_SCAN,
        // / Video display aspect ratio is 4x3 Letter box.
        E_VIDEO_ASPECT_RATIO_4X3_LETTER_BOX,
        // / Video display aspect ratio is 16x9 LetterBox.
        E_VIDEO_ASPECT_RATIO_16X9_PAN_SCAN,
        // / Video display aspect ratio is 4x3 combind.
        E_VIDEO_ASPECT_RATIO_4X3_COMBIND,
        // / Video display aspect ratio is 16x9 Bombind.
        E_VIDEO_ASPECT_RATIO_16X9_COMBIND
    }

    private static final int VIDEO_ASPECT_RATIO_AUTO = 0;

    private static final int VIDEO_ASPECT_RATIO_4X3 = 1;

    private static final int VIDEO_ASPECT_RATIO_16X9 = 2;

    private static final int VIDEO_ASPECT_RATIO_16X9_PILLARBOX = 3;

    private static final int VIDEO_ASPECT_RATIO_4X3_PAN_SCAN = 4;

    private static final int VIDEO_ASPECT_RATIO_4X3_LETTER_BOX = 5;

    private static final int VIDEO_ASPECT_RATIO_16X9_PAN_SCAN = 6;

    private static final int VIDEO_ASPECT_RATIO_4X3_COMBIND = 7;

    private static final int VIDEO_ASPECT_RATIO_16X9_COMBIND = 8;

    public boolean setVideoDisplayAspectRatio(EnumVideoAspectRatio aspectRatio) {
        Log.d(TAG, "aspect Ratio:" + aspectRatio);
        switch (aspectRatio) {
            case E_VIDEO_ASPECT_RATIO_AUTO:
                native_setVideoDisplayAspectRatio(VIDEO_ASPECT_RATIO_AUTO);
                break;
            case E_VIDEO_ASPECT_RATIO_4X3:
                native_setVideoDisplayAspectRatio(VIDEO_ASPECT_RATIO_4X3);
                break;
            case E_VIDEO_ASPECT_RATIO_16X9:
                native_setVideoDisplayAspectRatio(VIDEO_ASPECT_RATIO_16X9);
                break;
            default:
                native_setVideoDisplayAspectRatio(VIDEO_ASPECT_RATIO_AUTO);
                break;
        }
        return true;
    }

    @Override
    public void addTimedTextSource(String path, String mimeType) throws IOException,
            IllegalArgumentException, IllegalStateException {
        // TODO: check mineType
        File file = null;
        FileInputStream is = null;
        try {
            file = new File(path);
            if (file.exists()) {
                is = new FileInputStream(file);
                FileDescriptor fd = is.getFD();
                addTimedTextSource(fd, mimeType);
             } else {
                // We do not support the case where the path is not a file.
                throw new IOException(path);
             }
        } catch(Exception e) {
             Log.i("MMediaplayer","Exception: "+ e);
        } finally {
             if (is != null) {
                 is.close();
             }
        }
    }

    /*
     * Do not change these values (starting with INVOKE_ID) without updating
     * their counterparts in include/media/mediaplayer.h!
     */
    private static final int INVOKE_ID_GET_TRACK_INFO = 1;

    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE = 2;

    private static final int INVOKE_ID_ADD_EXTERNAL_SOURCE_FD = 3;

    private static final int INVOKE_ID_SELECT_TRACK = 4;

    private static final int INVOKE_ID_DESELECT_TRACK = 5;

    private static final int INVOKE_ID_SET_VIDEO_SCALE_MODE = 6;

    private static final int INVOKE_ID_GET_MS_TRACK_INFO = 1002;

    private static final int INVOKE_ID_SET_SUBTITLE_SOURCE_FD = 2066;

    @Override
    public void addTimedTextSource(FileDescriptor fd, long offset, long length, String mimeType)
            throws IllegalArgumentException, IllegalStateException {
        // TODO: check mineType
        Parcel request = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        try {
            request.writeInterfaceToken(IMEDIA_PLAYER);
            request.writeInt(INVOKE_ID_ADD_EXTERNAL_SOURCE_FD);
            request.writeFileDescriptor(fd);
            request.writeLong(offset);
            request.writeLong(length);
            request.writeString(mimeType);
            invoke(request, reply);
        } finally {
            request.recycle();
            reply.recycle();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.Q)
    public void setSubtitleDataSource(String url, long offset)
            throws IllegalArgumentException, IllegalStateException {
       FileInputStream is = null;
       FileDescriptor fd = null;
       FileDescriptor sub_fd = null;
       FileInputStream subis = null;
       try {
           if (url != null && !url.equals("")) {
               int strLength = url.lastIndexOf(".");
               if (strLength > 0) {
                   String externalFile = url.substring(strLength + 1).toLowerCase();
                   if (externalFile.equals("idx")) {
                       String subUri = url.substring(0,strLength) + ".sub";
                       File subfile = new File(subUri);
                       if (subfile.exists()) {
                           subis  = new FileInputStream(subfile);
                           sub_fd = subis.getFD();
                       }
                   }
               }

               File file = new File(url);
               if (file.exists()) {
                   is = new FileInputStream(file);
                   fd = is.getFD();
               } else {
                   // We do not support the case where the path is not a file.
                   throw new IOException(url);
               }
           }

            // TODO: check mineType
            Parcel request = Parcel.obtain();
            Parcel reply = Parcel.obtain();
            boolean isValueFD = false;
            try {
                request.writeInterfaceToken(IMEDIA_PLAYER);
                request.writeInt(INVOKE_ID_SET_SUBTITLE_SOURCE_FD);
                if (fd != null) {
                    isValueFD = true;
                    request.writeBoolean(isValueFD);
                    request.writeFileDescriptor(fd);
                    if (sub_fd != null) {
                        Log.i("MMediaplayer","vaild sub fd");
                        request.writeFileDescriptor(sub_fd);
                    } else {
                        Log.i("MMediaplayer","invaild sub fd");
                        request.writeFileDescriptor(fd);
                    }
                } else {
                    request.writeBoolean(isValueFD);
                }
                request.writeLong(0); // resolve
                request.writeLong(0); // resolve
                request.writeString(null); // resolve
                invoke(request, reply);
            } finally {
                request.recycle();
                reply.recycle();
            }
       } catch(Exception e) {
           Log.i("MMediaplayer","Exception: "+ e);
       } finally {
           try {
               if (subis != null) {
                   subis.close();
               }
           } catch(Exception e) {
               Log.i("MMediaplayer","Exception: "+ e);
           }
           try {
               if (is != null) {
                   is.close();
               }
           } catch(Exception e) {
               Log.i("MMediaplayer","Exception: "+ e);
           }
       }
    }
}
